package fnord.jovian;

import java.io.IOException;
import java.util.*;

import fnord.illuminati.*;
import fnord.illuminati.card.*;
import fnord.illuminati.order.AttackType;
import fnord.jovian.tango.*;
import fnord.jovian.tango.TheCard.TheCardInOut;
import fnord.jovian.tango.TheCard.TheCardOrientation;
import fnord.network.client.*;
import fnord.network.server.*;

public class TheGUICommunicator {

	private TheGUI _gui;
	private Server _server;
	private Client _client;
	private String _address = "localhost";
	private int _port = 4444, _players = 2;

	public TheGUICommunicator(TheGUI gui) {
		_gui = gui;
	}

	public void takeBoard(Board board) {
		Set<Player> players = board.getPlayers();
		Vector<TheCardTree> trees = new Vector<TheCardTree>();
		Vector<TheCardTree> miniTrees = new Vector<TheCardTree>();
		int player = 0, pCount = 0;
		Player myPlayer = _client.myPlayer();
		for (Iterator<Player> i = players.iterator(); i.hasNext(); ) {
			Player p = i.next();
			if (p.sameAs(myPlayer))
				player = pCount;
			TheIlluminatiCard illum = new TheIlluminatiCard(p.getIlluminati(),_gui.getBoard());
			TheCardTree tree = new TheCardTree(illum,_gui.getBoard(),false);
			TheMiniCard miniCard = illum.getMiniCard();
			TheCardTree mini = new TheCardTree(miniCard,_gui.getBoard(),true);
			addSubtree(tree,mini,illum,miniCard);
			trees.add(tree);
			miniTrees.add(mini);
			pCount++;
		}
		_gui.getBoard().setTrees(trees);
		_gui.getBoard().setField(setUpField(board.getField(),false));
		_gui.getBoard().setPlayer(player);
		_gui.getBoard().setMapTreesField(miniTrees,setUpField(board.getField(),true));
		if (board.getCurrentPlayer() != null)
			_gui.getBoard().setActive(myPlayer.sameAs(board.getCurrentPlayer()),board.getCurrentPlayer().getID());
		_gui.getBoard().setMoneyTransferOnly(board.getOnlyMoneyTransfer());
		_gui.getBoard().setSpendMoneyOnly(board.getOnlySpendMoney(),board.getAttackingCard(),board.getDefendingCard());
		_gui.getBoard().setStatus(board.getNotification());
		_gui.getBoard().startGame();
	}

	public TheField setUpField(Field field, boolean mini) {
		TheField theField = new TheField(_gui.getBoard(),mini);
		for (Iterator<GroupCard> cards = field.iterator(); cards.hasNext(); ) {
			GroupCard next = cards.next();
			TheGroupCard card = new TheGroupCard(next,inOutFromConnections(next),_gui.getBoard());
			if (mini)
				theField.addCard(card.getMiniCard());
			else
				theField.addCard(card);
		}
		return theField;
	}

	public void addSubtree(TheCardTree tree, TheCardTree mini, TheCard card, TheCard miniCard) {
		GroupCard gc = card.getCard();
		Set<Connection> connections = gc.getAllConnections();
		for (Iterator<Connection> i = connections.iterator(); i.hasNext(); ) {
			Connection connection = i.next();
			GroupCard child = gc.getChild(connection);
			if (child != null) {
				TheCardOrientation orientation = TheCardOrientation.SOUTH;
				switch(connection) {
				case SOUTH:
					orientation = TheCardOrientation.SOUTH;
					break;
				case EAST:
					orientation = TheCardOrientation.EAST;
					break;
				case NORTH:
					orientation = TheCardOrientation.NORTH;
					break;
				case WEST:
					orientation = TheCardOrientation.WEST;
					break;
				}
				TheGroupCard theChild = new TheGroupCard(child, inOutFromConnections(child), _gui.getBoard());
				tree.addChild(card, theChild, orientation);
				TheMiniCard miniChild = theChild.getMiniCard();
				mini.addChild(miniCard, miniChild, orientation);
				addSubtree(tree,mini,theChild,miniChild);
			}
		}
	}

	public TheCardInOut[] inOutFromConnections(GroupCard card) {
		Set<Connection> connections = card.getAllConnections();
		TheCardInOut[] inOut = new TheCardInOut[4];
		for (Iterator<Connection> i = connections.iterator(); i.hasNext(); ) {
			Connection connection = i.next();
			switch(connection) {
			case SOUTH:
				inOut[0] = TheCardInOut.OUT;
				break;
			case EAST:
				inOut[1] = TheCardInOut.OUT;
				break;
			case NORTH:
				inOut[2] = TheCardInOut.OUT;
				break;
			case WEST:
				inOut[3] = TheCardInOut.OUT;
				break;
			}
		}
		if (inOut[0] == null)
			inOut[0] = TheCardInOut.IN;
		for (int i = 0; i < 4; i++) {
			if (inOut[i] == null) {
				inOut[i] = TheCardInOut.BLANK;
			}
		}
		return inOut;
	}
	
	public Connection connectionFromOrient(TheCardOrientation inOut) {
		switch(inOut) {
		case EAST:
			return Connection.EAST;
		case NORTH:
			return Connection.NORTH;
		case WEST:
			return Connection.WEST;
		case SOUTH:
			return Connection.SOUTH;
		}
		return null;
	}

	public boolean takeChat(String chat) {
		_gui.getBoard().receiveChat(chat);
		return true;
	}

	public void launchServer() {
		try {
			_server = new Server(_players,_port);

			GetClientsThread gct = new GetClientsThread();
			gct.start();
			String temp = _address;
			_address = "localhost";
			launchClient();
			_address = temp;
			
		}
		catch(Exception e) {
			_gui.getBoard().setStatus("Invalid Server");
		}
	}

	public void launchClient() {
		try {
			_client = new Client(_address, _port, this);
		}
		catch(Exception e) {
			_gui.getBoard().setStatus("Could not connect to server");
		}
	}

	private class GetClientsThread extends Thread {
		public void run() {
			try {
				_server.getClients();
			} catch (IOException e) {
				_gui.getBoard().setStatus(e.getMessage());
			}
		}
	}

	public void sendAttackOrder(TheCard attacker, TheCard defender, AttackType type, Connection con, Collection<TheCard> cards) {
		try {
			int transferable = 0;
			Vector<GroupCard> gCards = new Vector<GroupCard>();
			for (Iterator<TheCard> i = cards.iterator(); i.hasNext();) {
				TheCard next = i.next();
				gCards.add(next.getCard());
				transferable += next.getCard().getTransferablePower();
			}
			_client.sendAttackOrder(_client.myPlayer(), attacker.getCard(), defender.getCard(), type, transferable, gCards, con);
		} catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
		}
	}

	public void sendMoneyOrder(TheCard attacker, TheCard defender, int amt) {
		try {
			_client.sendMoneyTransferOrder(_client.myPlayer(), attacker.getCard(), defender.getCard(), amt);
		} catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
		}
	}
	
	public void spendMoney(GroupCard attacker, GroupCard defender, int amt) {
		try {
			_client.sendSpendMoneyOrder(_client.myPlayer(), attacker, defender, amt);
		} catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
		}
	}
	
	public void sendTreeTransferOrder(GroupCard child, GroupCard to, TheCardOrientation orient) {
		try {
			_client.sendTreeTransferOrder(_client.myPlayer(), child, to, connectionFromOrient(orient));
		} catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
		}
	}
	
	public void setAddress(String address) {
		_address = address;
	}
	
	public String getAddress() {
		return _address;
	}
	
	public void setPort(int port) {
		_port = port;
	}
	
	public int getPort() {
		return _port;
	}
	
	public void setPlayers(int p) {
		_players = p;
	}
	
	public int getPlayers() {
		return _players;
	}
	
	public void sendChat(String s) {
		try {
	        _client.sendChat(_client.myPlayer(),s);
        } catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
        }
	}
	
	public void pass() {
		try {
	        _client.pass(_client.myPlayer());
        } catch (IOException e) {
			_gui.getBoard().setStatus(e.getMessage());
        }
	}
	
}
